#include "UdxDatasetSchema.h"
#include "UdxSchemaDescription.h"
#include "UdxDataSchemaApi.h"

#include <emscripten/bind.h>
using namespace emscripten;
using namespace NGIS::Data::Schema;

#include <wchar.h>
#include <locale.h>

namespace NGIS
{
	namespace Data
	{
		namespace Schema
		{
			//////////////////////////////////////////////////////////////////////////
			extern "C" NGISDATASCHEMA_API IUdxDatasetSchema* NGISDATASCHEMA_CALLCONV createUdxDatasetSchema(const char* pName)
			{
				CUdxDatasetSchema* pUdxDatasetSchema = new CUdxDatasetSchema(pName);
				return pUdxDatasetSchema;
			}

			extern "C" NGISDATASCHEMA_API INodeDescription* NGISDATASCHEMA_CALLCONV createUdxNodeDescription(ESchemaNodeType pNodeType, const char* pNodeInfo)
			{
				CUdxSchemaDescription* pNodeDescription = new CUdxSchemaDescription(pNodeType, pNodeInfo);
				return pNodeDescription;
			}

		}
	}
}

std::string ws2s(const std::wstring& ws)  
{  
	std::string curLocale = setlocale(LC_ALL, NULL); // curLocale = "C";  

	setlocale(LC_ALL, "chs");  

	const wchar_t* _Source = ws.c_str();  
	size_t _Dsize = 2 * ws.size() + 1;  
	char *_Dest = new char[_Dsize];  
	memset(_Dest,0,_Dsize);  
	wcstombs(_Dest,_Source,_Dsize);  
	std::string result = _Dest;  
	delete []_Dest;  

	setlocale(LC_ALL, curLocale.c_str());  

	return result;  
}  

std::wstring s2ws(const std::string& s)  
{  
	setlocale(LC_ALL, "chs");  

	const char* _Source = s.c_str();  
	size_t _Dsize = s.size() + 1;  
	wchar_t *_Dest = new wchar_t[_Dsize];  
	wmemset(_Dest, 0, _Dsize);  
	mbstowcs(_Dest,_Source,_Dsize);  
	std::wstring result = _Dest;  
	delete []_Dest;  

	setlocale(LC_ALL, "C");  

	return result;  
} 




float getVersion()
{
	return 1.1;
};

std::wstring getInfo()
{
	return L"我是谁？hahha";
}

int createUdxDatasetSchema_js()
{
	IUdxDatasetSchema* pUdxDatasetSchema = new CUdxDatasetSchema("UdxDeclaration");
	return (int)pUdxDatasetSchema;
}
	
int createUdxNodeDescription_js(ESchemaNodeType pNodeType, std::string pNodeInfo)
{
	CUdxSchemaDescription* pNodeDescription = new CUdxSchemaDescription(pNodeType, pNodeInfo.c_str());
	return (int)pNodeDescription;
}

int getRootNode(int pDatasetId)
{
	IUdxDatasetSchema* pDatasetNode = (IUdxDatasetSchema*)pDatasetId;
	IUdxNodeSchema* pNode = dynamic_cast<IUdxNodeSchema*>(pDatasetNode);
	return (int)pNode;
}

int addChildNode(int pParentNodeId, std::string pNodeName, ESchemaNodeType pNodeType, std::wstring pNodeInfo)
{
	IUdxNodeSchema* pParentNode = (IUdxNodeSchema*)pParentNodeId;
	std::string pNodeInfoStr = ws2s(pNodeInfo);
	IUdxNodeSchema* pNode = pParentNode->addChildNode(pNodeName.c_str(), pNodeType, pNodeInfoStr.c_str());
	return (int)pNode;
}

int removeChildNode(int pNodeId)
{
	IUdxNodeSchema* pNode = (IUdxNodeSchema*)pNodeId;
	IUdxNodeSchema* pParentNode = pNode->getParentNode();
	if (pParentNode!=NULL)
	{
		pParentNode->removeChildNode(pNode);
		return 0;
	}
	return -1;
}

int getNodeChildCount(int pNodeId)
{
	CUdxNodeSchema* pParentNode = (CUdxNodeSchema*)pNodeId;
	int childNodeCount = pParentNode->getChildNodeCount();
	return childNodeCount;
}

int getChildNode(int pParentNodeId, int idx)
{
	CUdxNodeSchema* pParentNode = (CUdxNodeSchema*)pParentNodeId;
	IUdxNodeSchema* pNode = pParentNode->getChildNode(idx);
	return (int)pNode;
}

std::string getNodeName(int pNodeId)
{
	CUdxNodeSchema* pNode = (CUdxNodeSchema*)pNodeId;
	std::string name = pNode->getName();
	return name;
}

std::string NodeType2String(ESchemaNodeType pNodeType)
{
	std::string typeStr = SchemaNodeType2String(pNodeType);
	return typeStr;
}

ESchemaNodeType getNodeType(int pNodeId)
{
	CUdxNodeSchema* pNode = (CUdxNodeSchema*)pNodeId;
	ESchemaNodeType nodeType = pNode->getDescription()->getKernelType();
	return nodeType;
}

std::wstring getNodeDescription(int pNodeId)
{
	CUdxNodeSchema* pNode = (CUdxNodeSchema*)pNodeId;
	std::string nodeDescription = pNode->getDescription()->getNodeDescription();
	std::wstring nodeDescriptionStr = s2ws(nodeDescription);
	return nodeDescriptionStr;
}

std::string getNodeConceptInfo(int pNodeId)
{
	IUdxNodeSchema* pNode = (IUdxNodeSchema*)pNodeId;
	std::string tag = pNode->getDescription()->getConceptTag();
	return tag;
}

std::string getNodeSpatialRefInfo(int pNodeId)
{
	IUdxNodeSchema* pNode = (IUdxNodeSchema*)pNodeId;
	std::string tag = pNode->getDescription()->getSpatialReferencefTag();
	return tag;
}

std::string getNodeUnitInfo(int pNodeId)
{
	IUdxNodeSchema* pNode = (IUdxNodeSchema*)pNodeId;
	std::string tag = pNode->getDescription()->getUnitTag();
	return tag;
}

std::string getNodeDataTemplateInfo(int pNodeId)
{
	IUdxNodeSchema* pNode = (IUdxNodeSchema*)pNodeId;
	std::string tag = pNode->getDescription()->getDataTemplateTag();
	return tag;
}

bool modifyNodeName(int pNodeId, std::wstring nodeName)
{
	IUdxNodeSchema* pNode = (IUdxNodeSchema*)pNodeId;
	if (pNode)
	{
		std::string nodeNameStr = ws2s(nodeName);
		pNode->modifyName(nodeNameStr.c_str());
	}
	return false;
}

bool modifyNodeDescription(int pNodeId, std::wstring descriptionInfo)
{
	IUdxNodeSchema* pNode = (IUdxNodeSchema*)pNodeId;
	if (pNode)
	{
		std::string description = ws2s(descriptionInfo);
		return pNode->getDescription()->modifyNodeDescription(description.c_str());
	}
	return false;
}

bool modifyNodeConceptInfo(int pNodeId, std::string tag)
{
	IUdxNodeSchema* pNode = (IUdxNodeSchema*)pNodeId;
	if (pNode)
	{
		return pNode->getDescription()->modifyConceptTag(tag.c_str());
	}
	return false;
}

bool modifyNodeSpatialRefInfo(int pNodeId, std::string tag)
{
	IUdxNodeSchema* pNode = (IUdxNodeSchema*)pNodeId;
	if (pNode)
	{
		return pNode->getDescription()->modifySpatialReferenceTag(tag.c_str());
	}
	return false;
}

bool modifyNodeUnitInfo(int pNodeId, std::string tag)
{
	IUdxNodeSchema* pNode = (IUdxNodeSchema*)pNodeId;
	if (pNode)
	{
		return pNode->getDescription()->modifyUnitTag(tag.c_str());
	}
	return false;
}

bool modifyNodeDataTemplateInfo(int pNodeId, std::string tag)
{
	IUdxNodeSchema* pNode = (IUdxNodeSchema*)pNodeId;
	if (pNode)
	{
		return pNode->getDescription()->modifyDataTemplateTag(tag.c_str());
	}
	return false;
}



std::string loadFromXmlStream(int datasetId, std::wstring xml_wstr)
{
	IUdxDatasetSchema* pDatasetNode = (IUdxDatasetSchema*)datasetId;
	if (pDatasetNode)
	{
		std::string xml_str = ws2s(xml_wstr);
		if (pDatasetNode->LoadFromXmlStream(xml_str.c_str()))
		{
			return "Parse OK";
		}
		else 
		{
			return "Parse Error";
		}
	}
	else
	{
		return "DatasetNode is NULL";
	}
}

std::wstring formatToXmlStream(int datasetId)
{
	IUdxDatasetSchema* pDatasetNode = (IUdxDatasetSchema*)datasetId;
	if (pDatasetNode)
	{
		std::string xml_str="";
		if (pDatasetNode->FormatToXmlStream(xml_str))
		{
			return s2ws(xml_str);
		}
		else
		{
			return L"Format Error";
		}
	}
	else
	{
		return L"DatasetNode is NULL";
	}
}

EMSCRIPTEN_BINDINGS(UDX_Module) {
	enum_<ESchemaNodeType>("SchemaNodeType")
		.value("EDTKT_INT", EDTKT_INT)
		.value("EDTKT_REAL", EDTKT_REAL)
		.value("EDTKT_VECTOR2", EDTKT_VECTOR2)
		.value("EDTKT_VECTOR3", EDTKT_VECTOR3)
		.value("EDTKT_VECTOR4", EDTKT_VECTOR4)
		.value("EDTKT_STRING", EDTKT_STRING)
		.value("EDTKT_INT_LIST", (ESchemaNodeType)(EDTKT_INT|EDTKT_LIST))
		.value("EDTKT_REAL_LIST", (ESchemaNodeType)(EDTKT_REAL|EDTKT_LIST))
		.value("EDTKT_VECTOR2_LIST", (ESchemaNodeType)(EDTKT_VECTOR2|EDTKT_LIST))
		.value("EDTKT_VECTOR3_LIST", (ESchemaNodeType)(EDTKT_VECTOR3|EDTKT_LIST))
		.value("EDTKT_VECTOR4_LIST", (ESchemaNodeType)(EDTKT_VECTOR4|EDTKT_LIST))
		.value("EDTKT_STRING_LIST", (ESchemaNodeType)(EDTKT_STRING|EDTKT_LIST))
		.value("EDTKT_NODE", EDTKT_NODE)
		.value("EDTKT_LIST", EDTKT_LIST)
		.value("EDTKT_MAP", EDTKT_MAP)
		.value("EDTKT_TABLE", EDTKT_TABLE)
		;
		
	function("getVersion", &getVersion);
	function("getInfo", &getInfo);
	
	function("createUdxDatasetSchema", &createUdxDatasetSchema_js);
	function("createUdxNodeDescription", &createUdxNodeDescription_js);
	function("getRootNode", &getRootNode);
	function("addChildNode", &addChildNode);
	function("removeChildNode", &removeChildNode);
	
	function("getNodeChildCount", &getNodeChildCount);
	function("getChildNode", &getChildNode);
	function("getNodeType", &getNodeType);

	function("NodeType2String", &NodeType2String);

	function("getNodeName", &getNodeName);
	function("getNodeDescription", &getNodeDescription);
	function("getNodeConceptInfo", &getNodeConceptInfo);
	function("getNodeSpatialRefInfo", &getNodeSpatialRefInfo);
	function("getNodeUnitInfo", &getNodeUnitInfo);
	function("getNodeDataTemplateInfo", &getNodeDataTemplateInfo);

	function("modifyNodeName", &modifyNodeName);
	function("modifyNodeDescription", &modifyNodeDescription);
	function("modifyNodeConceptInfo", &modifyNodeConceptInfo);
	function("modifyNodeSpatialRefInfo", &modifyNodeSpatialRefInfo);
	function("modifyNodeUnitInfo", &modifyNodeUnitInfo);
	function("modifyNodeDataTemplateInfo", &modifyNodeDataTemplateInfo);

	function("loadFromXmlStream", &loadFromXmlStream);
	function("formatToXmlStream", &formatToXmlStream);
}



